package ldh.fx.adminfx.commpont.grid;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.shape.Rectangle;
import javafx.util.Callback;
import ldh.fx.adminfx.util.AnchorPaneUtil;
import ldh.fx.adminfx.util.TableViewUtil;

import java.util.*;

public class FreezeGridTable<D> extends GridTable<D> {

    private TableView<D> leftFreezeTableView = new TableView<>();
    private TableView<D> rightFreezeTableView = new TableView<>();
    private ScrollBar hScrollBar = new ScrollBar();
    private ScrollBar vScrollBar = new ScrollBar();
    private boolean initScrollBar = false;
    private boolean initLeftFreezeBar = false;
    private boolean initRightFreezeBar = false;
    private static String LEFT_FREEZE = "leftFreeze";     // 冻结左边
    private static String RIGHT_FREEZE = "rightFreeze";   // 冻结右边
    private static String UN_FREEZE = "unFreeze";  // 解冻
    private boolean isInitEnd = false;

    public FreezeGridTable() {
        leftFreezeTableView.getStyleClass().add("freeze-table");
        rightFreezeTableView.getStyleClass().add("freeze-table");

        hScrollBar.setVisible(false);
        hScrollBar.setOrientation(Orientation.HORIZONTAL);
        hScrollBar.getStyleClass().add("table-scroll-bar");
        AnchorPaneUtil.anchor(hScrollBar, null, 0d, 0d, 0d);

        vScrollBar.setVisible(false);
        vScrollBar.setOrientation(Orientation.VERTICAL);
        vScrollBar.getStyleClass().add("table-scroll-bar");
        AnchorPaneUtil.anchor(vScrollBar, 0d, 0d, 0d, null);

        freezePane.getChildren().addAll(hScrollBar, vScrollBar);

        leftFreezeTableView.setManaged(false);
        rightFreezeTableView.setManaged(false);

        tablePane.getChildren().add(0, leftFreezeTableView);
        tablePane.getChildren().add(rightFreezeTableView);

        initFreezeTableView();

        tableView.getItems().addListener((ListChangeListener<D>)l->{
            while (l.next()) {
                if (l.wasAdded()) {
                    for (D d : l.getAddedSubList()) {
                        leftFreezeTableView.getItems().add(d);
                        rightFreezeTableView.getItems().add(d);
                    }
                } else if (l.wasRemoved()) {
                    for (D d : l.getRemoved()) {
                        leftFreezeTableView.getItems().remove(d);
                        rightFreezeTableView.getItems().remove(d);
                    }
                }
            }
            if(leftFreezeTableView.getItems().size() > 0) {
                leftFreezeTableView.setManaged(true);
            } else {
                leftFreezeTableView.setManaged(false);
            }
            if(rightFreezeTableView.getItems().size() > 0) {
                rightFreezeTableView.setManaged(true);
            } else {
                rightFreezeTableView.setManaged(false);
            }
        });

        tableView.getChildrenUnmodifiable().addListener((ListChangeListener)l->{
            initScrollBar();
        });

        Callback<TableView<D>, Boolean> sortCallBack = sortCallBack(tableView);
        tableView.sortPolicyProperty().set(sortCallBack);
        leftFreezeTableView.sortPolicyProperty().set(sortCallBack(leftFreezeTableView));
        rightFreezeTableView.sortPolicyProperty().set(sortCallBack(rightFreezeTableView));

        selectedBind();
    }

    public void alignment(Pos alignment) {
        super.alignment(alignment);
        TableViewUtil.alignment(alignment, leftFreezeTableView);
        TableViewUtil.alignment(alignment, rightFreezeTableView);
    }

    public final TableView<D> getLeftFreezeTableView() {
        return leftFreezeTableView;
    }

    public final TableView<D> getRightFreezeTableView() {
        return rightFreezeTableView;
    }

    public final ObservableList<TableColumn<D,?>> getLeftColumns() {
        return leftFreezeTableView.getColumns();
    }

    public final ObservableList<TableColumn<D,?>> getRightColumns() {
        return rightFreezeTableView.getColumns();
    }

    private void initScrollBar() {
        if (!initScrollBar) {
            ScrollBar verticalBar = getScrollBar(tableView, Orientation.VERTICAL);
            ScrollBar horizontalBar = getScrollBar(tableView, Orientation.HORIZONTAL);
            if (verticalBar != null && horizontalBar != null) {
                initScrollBar = true;
            } else {
                return;
            }
            hScrollBar.visibleProperty().bind(horizontalBar.visibleProperty());
            hScrollBar.valueProperty().bindBidirectional(horizontalBar.valueProperty());
            hScrollBar.maxProperty().bindBidirectional(horizontalBar.maxProperty());
            hScrollBar.minProperty().bindBidirectional(horizontalBar.minProperty());

            vScrollBar.valueProperty().bindBidirectional(verticalBar.valueProperty());
            vScrollBar.visibleProperty().bind(verticalBar.visibleProperty());
            vScrollBar.maxProperty().bindBidirectional(verticalBar.maxProperty());
            vScrollBar.minProperty().bindBidirectional(verticalBar.minProperty());

            hiddenScrollBar(verticalBar);
            hiddenScrollBar(horizontalBar);
        }
    }

    private void initFreezeScrollBar(TableView<D> freezeTableView) {
        if ((freezeTableView == leftFreezeTableView && !initLeftFreezeBar) || (freezeTableView == rightFreezeTableView && !initRightFreezeBar)) {
            ScrollBar verticalBar = getScrollBar(freezeTableView, Orientation.VERTICAL);
            ScrollBar horizontalBar = getScrollBar(freezeTableView, Orientation.HORIZONTAL);
            if (verticalBar != null && horizontalBar != null) {
                if(freezeTableView == leftFreezeTableView) initLeftFreezeBar = true;
                if(freezeTableView == rightFreezeTableView) initRightFreezeBar = true;
            } else {
                return;
            }
            hiddenScrollBar(verticalBar);
            hiddenScrollBar(horizontalBar);
            verticalBar.valueProperty().bindBidirectional(vScrollBar.valueProperty());
        }
    }

    private void freezeTable(TableView<D> freezeTableView, TableColumn<D,?> c) {
        isInitEnd = true;
        boolean isLeft = freezeTableView == leftFreezeTableView ? true : false;

        List<TableColumn<D, ?>> freeColumns = new ArrayList();
        boolean isStart = false;
        for(TableColumn c1 : tableView.getColumns()) {
            if(isLeft) {
                if (c1 != c) {
                    freeColumns.add(c1);
                } else {
                    freeColumns.add(c1);
                    break;
                }
            } else {
                if (c1 == c || isStart) {
                    isStart = true;
                    freeColumns.add(c1);
                }
            }
        }
        tableView.getColumns().forEach(c2->removeFreezeItem(c2, isLeft ? LEFT_FREEZE : RIGHT_FREEZE));
        tableView.getColumns().removeAll(freeColumns);
        freezeTableView.getColumns().addAll(freeColumns);
        freezeTableView.getColumns().forEach(c2->removeFreezeItem(c2, isLeft ? RIGHT_FREEZE : LEFT_FREEZE));
        freezeTableView.getColumns().forEach(c2->unFreezeTableColumn(freezeTableView, c2));
        if (isLeft) {
            if (!tablePane.getChildren().contains(freezeTableView)) {
                tablePane.getChildren().add(0, freezeTableView);
            }
        } else {
            if (!tablePane.getChildren().contains(freezeTableView)) {
                tablePane.getChildren().add(tablePane.getChildren().size(), freezeTableView);
            }
        }
    }

    private void unFreezeTable(TableView<D> freezeTableView, TableColumn<D,?> c) {
        boolean isLeft = freezeTableView == leftFreezeTableView ? true : false;
        tablePane.getChildren().remove(freezeTableView);

        List<TableColumn<D, ?>> removeTableColumns = new ArrayList<>();
        for(TableColumn<D, ?> c2 : freezeTableView.getColumns()) {
            removeTableColumns.add(c2);
        }
        freezeTableView.getColumns().removeAll(removeTableColumns);

        removeTableColumns.forEach(c1->removeFreezeItem(c1, UN_FREEZE));
        if (isLeft) {
            tableView.getColumns().addAll(0, removeTableColumns);
        } else {
            tableView.getColumns().addAll(removeTableColumns);
        }

        removeTableColumns.forEach(c2->freezeTableColumn(c2, freezeTableView != leftFreezeTableView && leftFreezeTableView.getColumns().size() == 0,
                freezeTableView != rightFreezeTableView && rightFreezeTableView.getColumns().size() == 0, false));
        tableView.getColumns().forEach(c2->freezeTableColumn(c2, freezeTableView == leftFreezeTableView, freezeTableView == rightFreezeTableView, false));
    }

    private void removeFreezeItem(TableColumn<D,?> c, String... userDatas) {
        ContextMenu contextMenu = c.getContextMenu();
        if (contextMenu == null || contextMenu.getItems().size() < 1) {
            return;
        }
        List<MenuItem> removes = new ArrayList<>();
        for(MenuItem menuItem : contextMenu.getItems()) {
            for(String userData : userDatas) {
                if (menuItem.getUserData().toString().equals(userData)) {
                    removes.add(menuItem);
                    break;
                }
            }
        }
        if (removes.size() > 0) contextMenu.getItems().removeAll(removes);
    }

    private void freezeTableColumn(TableColumn<D,?> c, boolean isLeft, boolean isRight, boolean isUnFreeze) {
        ContextMenu contextMenu = getContextMenu(c);
        contextMenu.setUserData(c.getText());

        if (isUnFreeze && !hasMenuItem(contextMenu, UN_FREEZE)) {
            MenuItem rightMenuItem = new MenuItem("解冻");
            rightMenuItem.setUserData(UN_FREEZE);
            rightMenuItem.setOnAction(e->{
                if (isLeft) {
                    unFreezeTable(leftFreezeTableView, c);
                    tableView.getColumns().forEach(c2->removeFreezeItem(c2, LEFT_FREEZE));
                } else {
                    unFreezeTable(rightFreezeTableView, c);
                    tableView.getColumns().forEach(c2->removeFreezeItem(c2, RIGHT_FREEZE));
                }

            });
            contextMenu.getItems().add(0, rightMenuItem);
            return;
        }

        if (isLeft && !hasMenuItem(contextMenu, LEFT_FREEZE)) {
            MenuItem leftMenuItem = new MenuItem("左冻结");
            leftMenuItem.setUserData(LEFT_FREEZE);
            leftMenuItem.setOnAction(e->{
                freezeTable(leftFreezeTableView, c);
            });
            contextMenu.getItems().add(0, leftMenuItem);
        }

        if (isRight && !hasMenuItem(contextMenu, RIGHT_FREEZE)) {
            MenuItem rightMenuItem = new MenuItem("右冻结");
            rightMenuItem.setUserData(RIGHT_FREEZE);
            rightMenuItem.setOnAction(e->{
                freezeTable(rightFreezeTableView, c);
            });
            contextMenu.getItems().add(0, rightMenuItem);
        }
    }

    private boolean hasMenuItem(ContextMenu contextMenu, String userData) {
        if (contextMenu == null) return false;
        for(MenuItem menuItem : contextMenu.getItems()) {
            if (menuItem.getUserData().toString().equals(userData)) return true;
        }
        return false;
    }

    private void unFreezeTableColumn(TableView<D> freezeTableView, TableColumn<D,?> c) {
        ContextMenu contextMenu = getContextMenu(c);
        contextMenu.setUserData(c.getText());
        MenuItem menuItem = new MenuItem("解冻");
        menuItem.setUserData(UN_FREEZE);
        menuItem.setOnAction(e->{
            unFreezeTable(freezeTableView, c);
        });
        contextMenu.getItems().add(0, menuItem);
    }

    private ScrollBar getScrollBar(TableView tableView, Orientation orientation) {
        Set<Node> nodes = tableView.lookupAll(".scroll-bar");
        for(Node node : nodes) {
            ScrollBar scrollBar = (ScrollBar) node;
            if (scrollBar.getOrientation() == orientation) {
                return scrollBar;
            }
        }
        return null;
    }

    private ContextMenu getContextMenu(TableColumn<D, ?> tableColumn) {
        ContextMenu contextMenu = tableColumn.getContextMenu();
        if (contextMenu == null) {
            contextMenu = new ContextMenu();
            tableColumn.setContextMenu(contextMenu);
        }
        return contextMenu;
    }

    private void hiddenScrollBar(ScrollBar scrollBar) {
        scrollBar.setVisible(false);
        scrollBar.setPrefHeight(0);
        scrollBar.setMaxHeight(0);

        scrollBar.setPrefWidth(0);
        scrollBar.setMaxWidth(0);
    }

    private Callback<TableView<D>, Boolean> sortCallBack(TableView<D> t) {
        return new Callback<TableView<D>, Boolean>() {
            @Override
            public Boolean call(TableView<D> dTableView) {
                Comparator<D> comparator = (r1, r2)
                        -> t.getComparator() == null ? 0 : t.getComparator().compare(r1, r2); //columns are sorted: sort accordingly
                FXCollections.sort(tableView.getItems(), comparator);
                FXCollections.sort(leftFreezeTableView.getItems(), comparator);
                FXCollections.sort(rightFreezeTableView.getItems(), comparator);
                return true;
            }
        };
    }

    private void initFreezeTableView() {
        tableView.getColumns().addListener((ListChangeListener<TableColumn<D, ?>>)l->{
            if (isInitEnd) return;
            while (l.next()) {
                if (l.wasAdded()) {
                    for (TableColumn<D, ?> c : l.getAddedSubList()) {
                        freezeTableColumn(c, true, true, false);
                    }
                }
            }
        });

        leftFreezeTableView.getColumns().addListener((ListChangeListener<TableColumn<D, ?>>)l->{
            calacFreezeTableViewWidth(leftFreezeTableView);
            leftFreezeTableView.setManaged(true);
            leftFreezeTableView.getChildrenUnmodifiable().addListener((ListChangeListener) l2->{
                initFreezeScrollBar(leftFreezeTableView);
            });
            if (isInitEnd) return;
            while (l.next()) {
                if (l.wasAdded()) {
                    for (TableColumn<D, ?> c : l.getAddedSubList()) {
                        freezeTableColumn(c, true, false, true);
                    }
                }
            }
        });

        rightFreezeTableView.getColumns().addListener((ListChangeListener<TableColumn<D, ?>>)l->{
            calacFreezeTableViewWidth(rightFreezeTableView);
            rightFreezeTableView.setManaged(true);
            rightFreezeTableView.getChildrenUnmodifiable().addListener((ListChangeListener) l2->{
                initFreezeScrollBar(rightFreezeTableView);
            });
            if (isInitEnd) return;
            while (l.next()) {
                if (l.wasAdded()) {
                    for (TableColumn<D, ?> c : l.getAddedSubList()) {
                        freezeTableColumn(c, false, true, true);
                    }
                }
            }
        });
    }

    private void calacFreezeTableViewWidth(TableView<D> freezeTableView) {
        freezeTableView.getColumns().addListener((ListChangeListener) l ->{
            double width = 0;
            for(TableColumn<D, ?> tc : freezeTableView.getColumns()) {
                width += tc.getPrefWidth();
            }
            freezeTableView.setPrefWidth(width);
            freezeTableView.setMinWidth(width);
        });
    }

    private void selectedBind() {
        leftFreezeTableView.getSelectionModel().selectedItemProperty().addListener((ob, n, o)->{
            if (leftFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                tableView.getSelectionModel().select(leftFreezeTableView.getSelectionModel().getSelectedItem());
            }

            if (rightFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                rightFreezeTableView.getSelectionModel().select(leftFreezeTableView.getSelectionModel().getSelectedItem());
            }
        });

        rightFreezeTableView.getSelectionModel().selectedItemProperty().addListener((ob, n, o)->{
            if (rightFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                tableView.getSelectionModel().select(rightFreezeTableView.getSelectionModel().getSelectedItem());
            }

            if (leftFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                leftFreezeTableView.getSelectionModel().select(rightFreezeTableView.getSelectionModel().getSelectedItem());
            }
        });

        tableView.getSelectionModel().selectedItemProperty().addListener((ob, n, o)->{
            if (leftFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                leftFreezeTableView.getSelectionModel().select(tableView.getSelectionModel().getSelectedItem());
            }
            if (rightFreezeTableView.getSelectionModel().getSelectedItem() != tableView.getSelectionModel().getSelectedItem()) {
                rightFreezeTableView.getSelectionModel().select(tableView.getSelectionModel().getSelectedItem());
            }
        });
    }

}
